#include "WindowManager.h"
#include "DataManager.h"
#include "resource.h"
#include "version.h"
#include <iostream>
#include <sstream>

#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "dcomp.lib")
#pragma comment(lib, "windowscodecs.lib")
#pragma comment(lib, "msimg32.lib") // For AlphaBlend function

// Define D2D error constant if not available
#ifndef D2DERR_RECREATE_TARGET
#define D2DERR_RECREATE_TARGET MAKE_HRESULT(SEVERITY_ERROR, FACILITY_D2D, 0x15)
#endif

WindowManager::WindowManager()
    : m_hwnd(nullptr)
    , m_hInstance(GetModuleHandle(nullptr))
    , m_isNekoActive(false)  // Start with inactive state (neko_down) by default
    , m_dataManager(nullptr)
    , m_trayIconCreated(false)
    , m_resetCallback(nullptr)
    , m_useDirectComposition(false)
    , m_isDragging(false)
    , m_dragOffsetX(0)
    , m_dragOffsetY(0)
    , m_hBitmapNekoDown(nullptr)
    , m_hBitmapNekoUp(nullptr)
{
    // Create and initialize data manager
    m_dataManager = new DataManager();
}

WindowManager::~WindowManager() {
    // Remove system tray icon
    RemoveSystemTray();
    
    // Clean up GDI images
    CleanupGDIImages();
    
    // Clean up data manager
    if (m_dataManager) {
        delete m_dataManager;
        m_dataManager = nullptr;
    }
    
    // Clean up DirectComposition resources
    CleanupDirectComposition();
}

bool WindowManager::Initialize() {
    // Try DirectComposition first (may fail in VMs)
    if (InitializeDirectComposition()) {
        m_useDirectComposition = true;
    } else {
        // Fallback to Direct2D with transparent window
        if (InitializeDirect2DFallback()) {
            m_useDirectComposition = false;
        } else {
            // Both DirectComposition and Direct2D failed - use GDI only
            m_useDirectComposition = false;
        }
    }
    
    // Always try to load GDI images for maximum compatibility
    LoadGDIImages();
    
    // Initialize data manager
    if (m_dataManager && !m_dataManager->Initialize()) {
        return false;
    }
    
    return true;
}

void WindowManager::CreateMainWindow() {
    
    const char* className = "NekoPaWindow";
    
    // Register window class
    WNDCLASSA wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = m_hInstance;
    wc.lpszClassName = className;
    wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wc.hbrBackground = nullptr; // No background brush for transparent window
    
    if (!RegisterClassA(&wc)) {
            return;
    }
    
    // Calculate window position
    int windowX, windowY;
    CalculateBottomRightPosition(windowX, windowY);
    

    
    // Create window based on rendering mode
    DWORD exStyle = WS_EX_TOPMOST | WS_EX_TOOLWINDOW;
    if (!m_useDirectComposition) {
        exStyle |= WS_EX_LAYERED; // Add layered style for fallback mode
    }
    
        m_hwnd = CreateWindowExA(
        exStyle,
            className,
            "NekoPa",
            WS_POPUP | WS_VISIBLE,
            windowX, windowY,
        80, 80, // Fixed size
        nullptr, nullptr, m_hInstance, nullptr
        );
        
        if (!m_hwnd) {
            return;
    }
    
    // Set the WindowManager pointer
        SetWindowLongPtr(m_hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
}

void WindowManager::ShowWindow() {
    
    if (m_hwnd) {
        if (m_useDirectComposition) {
            // DirectComposition mode - but we'll force Direct2D fallback in paint
            // Still try to create swap chain for potential future use
            CreateSwapChain(); // Don't fail if this doesn't work
            
            // Load images after DirectComposition is setup
            LoadImagesFromResource();
            
            // Ensure window has layered style for text overlay compatibility
            LONG_PTR exStyle = GetWindowLongPtr(m_hwnd, GWL_EXSTYLE);
            if (!(exStyle & WS_EX_LAYERED)) {
                SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED);
            }
            
        ::ShowWindow(m_hwnd, SW_SHOW);
            
            // Set transparent color key for text overlay
            SetLayeredWindowAttributes(m_hwnd, RGB(0, 0, 0), 0, LWA_COLORKEY);
        } else {
            // Direct2D fallback mode
            LoadImagesFromResource();
            
            ::ShowWindow(m_hwnd, SW_SHOW);
            
            // Set transparent color key after window is shown
            SetLayeredWindowAttributes(m_hwnd, RGB(0, 0, 0), 0, LWA_COLORKEY);
            
            // Force a redraw to apply transparency
            InvalidateRect(m_hwnd, nullptr, FALSE);
            UpdateWindow(m_hwnd);
        }
        
        UpdateWindow(m_hwnd);
        EnsureAlwaysOnTop(); // Ensure it stays on top when shown
    }
}

bool WindowManager::InitializeDirectComposition() {
    HRESULT hr = S_OK;
    
    // Initialize COM
    hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) {
        return false;
    }
    
    // Create D3D11 device
    D3D_FEATURE_LEVEL featureLevels[] = {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0
    };
    
    hr = D3D11CreateDevice(
        nullptr,                    // Adapter
        D3D_DRIVER_TYPE_HARDWARE,   // Driver Type
        nullptr,                    // Software
        D3D11_CREATE_DEVICE_BGRA_SUPPORT, // Flags - needed for Direct2D
        featureLevels,              // Feature Levels
        ARRAYSIZE(featureLevels),   // Num Feature Levels
        D3D11_SDK_VERSION,          // SDK Version
        &m_d3dDevice,               // Device
        nullptr,                    // Feature Level
        nullptr                     // Device Context
    );
    if (FAILED(hr)) {
        return false;
    }
    
    // Get DXGI device
    hr = m_d3dDevice.As(&m_dxgiDevice);
    if (FAILED(hr)) return false;
    
    // Get DXGI factory
    IDXGIAdapter* adapter;
    hr = m_dxgiDevice->GetAdapter(&adapter);
    if (FAILED(hr)) return false;
    
    hr = adapter->GetParent(IID_PPV_ARGS(&m_dxgiFactory));
    adapter->Release();
    if (FAILED(hr)) return false;
    
    // Create Direct2D factory
    hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, m_d2dFactory.GetAddressOf());
    if (FAILED(hr)) return false;
    
    // Create Direct2D device
    hr = m_d2dFactory->CreateDevice(m_dxgiDevice.Get(), &m_d2dDevice);
    if (FAILED(hr)) return false;
    
    // Create Direct2D device context
    hr = m_d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &m_d2dContext);
    if (FAILED(hr)) return false;
    
    // Create DirectComposition device
    hr = DCompositionCreateDevice(m_dxgiDevice.Get(), IID_PPV_ARGS(&m_dcompDevice));
    if (FAILED(hr)) {
        return false;
    }
    
    // Create WIC factory for PNG loading
    hr = CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER,
                         IID_PPV_ARGS(&m_wicFactory));
    if (FAILED(hr)) return false;
    
    return true;
}

LRESULT CALLBACK WindowManager::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    WindowManager* pThis = nullptr;
    
    if (uMsg == WM_NCCREATE) {
        CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
        pThis = reinterpret_cast<WindowManager*>(pCreate->lpCreateParams);
        SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
    } else {
        pThis = reinterpret_cast<WindowManager*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
    }
    
    if (pThis) {
        return pThis->HandleMessage(uMsg, wParam, lParam);
    }
    
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

LRESULT WindowManager::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(m_hwnd, &ps);
            
            // Rendering with VM compatibility optimization
            if (m_useDirectComposition && m_d2dFactory && m_dcompDevice) {
                // DirectComposition may block GDI text overlay in VMs
                // Use Direct2D fallback mode for better text compatibility
                if (DrawDirect2D(hdc)) {
                    DrawCountOverlay(hdc);
                } else {
                    DrawGDIFallback(hdc);
                }
            } else if (m_d2dFactory) {
                // Direct2D fallback mode
                if (DrawDirect2D(hdc)) {
                    // Direct2D succeeded, now add GDI text overlay for count display
                    DrawCountOverlay(hdc);
                } else {
                    DrawGDIFallback(hdc); // GDI fallback (includes count display)
                }
            } else {
                // Pure GDI mode - all graphics APIs failed
                DrawGDIFallback(hdc); // GDI fallback (includes count display)
            }
            
            EndPaint(m_hwnd, &ps);
            return 0;
        }
        
        case WM_TIMER: {
            if (wParam == NEKO_TIMER_ID) {
                // Timer expired, reset neko to inactive state
                OnNekoAnimationTimer();
                return 0;
            }
            break;
        }
        
        case WM_TRAYICON: {
            // Handle system tray icon messages
            HandleTrayIcon(wParam, lParam);
            return 0;
        }
        
        case WM_LBUTTONDOWN: {
            // Start window dragging
            SetCapture(m_hwnd);
            
            // Begin drag operation
            POINT pt;
            GetCursorPos(&pt);
            RECT windowRect;
            GetWindowRect(m_hwnd, &windowRect);
            
            // Calculate offset from cursor to window top-left
            m_dragOffsetX = pt.x - windowRect.left;
            m_dragOffsetY = pt.y - windowRect.top;
            m_isDragging = true;
            
            return 0;
        }
        
        case WM_LBUTTONUP: {
            // End window dragging
            if (m_isDragging) {
                ReleaseCapture();
                m_isDragging = false;
            }
            return 0;
        }
        
        case WM_MOUSEMOVE: {
            // Handle window dragging
            if (m_isDragging && (wParam & MK_LBUTTON)) {
                POINT pt;
                GetCursorPos(&pt);
                
                // Calculate new window position
                int newX = pt.x - m_dragOffsetX;
                int newY = pt.y - m_dragOffsetY;
                
                // Move window to new position
                SetWindowPos(m_hwnd, nullptr, newX, newY, 0, 0, 
                           SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
                
                return 0;
            }
            break;
        }
        
        case WM_CLOSE:
            // Hide window instead of destroying it (minimize to tray)
            ::ShowWindow(m_hwnd, SW_HIDE);

            return 0;
            
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    
    return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}

// System tray implementation
bool WindowManager::CreateSystemTray() {
    if (!m_hwnd) return false;
    
    // Create notification icon data structure
    NOTIFYICONDATAA nid = {};
    nid.cbSize = sizeof(NOTIFYICONDATAA);
    nid.hWnd = m_hwnd;
    nid.uID = 1;
    nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
    nid.uCallbackMessage = WM_TRAYICON; // Custom message defined in resource.h
    
    // Load icon from resources (fallback to default if needed)
    nid.hIcon = LoadIconA(m_hInstance, MAKEINTRESOURCEA(1)); // Use ICO resource ID 1
    if (!nid.hIcon) {
        nid.hIcon = LoadIconA(nullptr, IDI_APPLICATION); // Default application icon
    }
    
    // Set tooltip text
    strcpy_s(nid.szTip, "NekoPa - Keyboard Click Counter");
    
    // Add the icon to system tray
    if (Shell_NotifyIconA(NIM_ADD, &nid)) {
        m_trayIconCreated = true;
        return true;
    } else {
        return false;
    }
}

void WindowManager::RemoveSystemTray() {
    if (m_trayIconCreated && m_hwnd) {
        
        NOTIFYICONDATAA nid = {};
        nid.cbSize = sizeof(NOTIFYICONDATAA);
        nid.hWnd = m_hwnd;
        nid.uID = 1;
        
        Shell_NotifyIconA(NIM_DELETE, &nid);
        m_trayIconCreated = false;
    }
}
void WindowManager::EnsureAlwaysOnTop() {
    if (m_hwnd) {
        SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, 
                     SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
    }
}

void WindowManager::UpdateClickCount(int count) {
    if (m_hwnd) {
        // Trigger window redraw
        InvalidateRect(m_hwnd, nullptr, FALSE);
        UpdateWindow(m_hwnd);
    }
}

void WindowManager::SetNekoState(bool active) {
    if (active) {
        // Set to active state (neko_up)
        m_isNekoActive = true;
        if (m_hwnd) {
            // Force window redraw (don't erase background - let Direct2D handle it)
            InvalidateRect(m_hwnd, nullptr, FALSE); // FALSE = don't erase background
            UpdateWindow(m_hwnd);
            // Set timer to automatically reset to inactive after 0.15 seconds
            SetTimer(m_hwnd, NEKO_TIMER_ID, NEKO_ANIMATION_DURATION, nullptr);
        }
    } else {
        // Set to inactive state (neko_down)
            m_isNekoActive = false;
            if (m_hwnd) {
            // Force window redraw (don't erase background - let Direct2D handle it)
            InvalidateRect(m_hwnd, nullptr, FALSE); // FALSE = don't erase background
            UpdateWindow(m_hwnd);
                // Kill the timer when manually setting to inactive
                KillTimer(m_hwnd, NEKO_TIMER_ID);
        }
    }
}

void WindowManager::CalculateBottomRightPosition(int& x, int& y) {
    RECT workArea;
    SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0);
    
    x = workArea.right - 80 - 10;
    y = workArea.bottom - 80 - 10;
}

void WindowManager::CleanupDirectComposition() {
    // Release images
    m_nekoDownImage.Reset();
    m_nekoUpImage.Reset();
    
    // Release DirectComposition resources
    m_dcompVisual.Reset();
    m_dcompTarget.Reset();
    m_dcompDevice.Reset();
    
    // Release Direct2D resources
    m_d2dContext.Reset();
    m_d2dDevice.Reset();
    m_d2dFactory.Reset();
    
    // Release swap chain
    m_swapChain.Reset();
    
    // Release D3D/DXGI resources
    m_dxgiFactory.Reset();
    m_dxgiDevice.Reset();
    m_d3dDevice.Reset();
    
    // Release WIC factory
    m_wicFactory.Reset();
    
    // Uninitialize COM
    CoUninitialize();
}

bool WindowManager::CreateSwapChain() {
    if (!m_hwnd || !m_dcompDevice || !m_dxgiFactory) return false;
    
    HRESULT hr = S_OK;
    
    // Create swap chain description
    DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
    swapChainDesc.Width = 80;
    swapChainDesc.Height = 80;
    swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    swapChainDesc.Stereo = FALSE;
    swapChainDesc.SampleDesc.Count = 1;
    swapChainDesc.SampleDesc.Quality = 0;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.BufferCount = 2;
    swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
    swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; // Key for transparency
    swapChainDesc.Flags = 0;
    
    // Create swap chain for composition
    hr = m_dxgiFactory->CreateSwapChainForComposition(
        m_dxgiDevice.Get(),
        &swapChainDesc,
        nullptr,
        &m_swapChain
    );
    if (FAILED(hr)) return false;
    
    // Create DirectComposition target for the window
    hr = m_dcompDevice->CreateTargetForHwnd(m_hwnd, TRUE, &m_dcompTarget);
    if (FAILED(hr)) {
        return false;
    }
    
    // Create visual
    hr = m_dcompDevice->CreateVisual(&m_dcompVisual);
    if (FAILED(hr)) return false;
    
    // Set swap chain as content of the visual
    hr = m_dcompVisual->SetContent(m_swapChain.Get());
    if (FAILED(hr)) return false;
    
    // Set visual as root of the target
    hr = m_dcompTarget->SetRoot(m_dcompVisual.Get());
    if (FAILED(hr)) return false;
    
    // Commit the changes
    hr = m_dcompDevice->Commit();
    if (FAILED(hr)) return false;
    
    return true;
}

bool WindowManager::LoadImagesFromResource() {
    // Load neko down image
    m_nekoDownImage = LoadPNGFromResource(IDR_NEKO_DOWN_PNG);
    
    // Load neko up image  
    m_nekoUpImage = LoadPNGFromResource(IDR_NEKO_UP_PNG);
    
    return (m_nekoDownImage != nullptr && m_nekoUpImage != nullptr);
}

ComPtr<ID2D1Bitmap> WindowManager::LoadPNGFromResource(int resourceId) {
    if (!m_wicFactory) return nullptr;
    
    // Find and load the resource
    HRSRC hResource = FindResourceA(m_hInstance, MAKEINTRESOURCEA(resourceId), "PNG");
    if (!hResource) return nullptr;
    
    DWORD imageSize = SizeofResource(m_hInstance, hResource);
    if (!imageSize) return nullptr;
    
    HGLOBAL hResourceData = LoadResource(m_hInstance, hResource);
    if (!hResourceData) return nullptr;
    
    void* pImageData = LockResource(hResourceData);
    if (!pImageData) return nullptr;
    
    // Create a stream from the resource data
    ComPtr<IWICStream> stream;
    HRESULT hr = m_wicFactory->CreateStream(&stream);
    if (FAILED(hr)) return nullptr;
    
    hr = stream->InitializeFromMemory(static_cast<BYTE*>(pImageData), imageSize);
    if (FAILED(hr)) return nullptr;
    
    // Create decoder
    ComPtr<IWICBitmapDecoder> decoder;
    hr = m_wicFactory->CreateDecoderFromStream(stream.Get(), nullptr, 
                                              WICDecodeMetadataCacheOnDemand, &decoder);
    if (FAILED(hr)) return nullptr;
    
    // Get the first frame
    ComPtr<IWICBitmapFrameDecode> frame;
    hr = decoder->GetFrame(0, &frame);
    if (FAILED(hr)) return nullptr;
    
    // Create format converter to ensure BGRA format for Direct2D
    ComPtr<IWICFormatConverter> converter;
    hr = m_wicFactory->CreateFormatConverter(&converter);
    if (FAILED(hr)) return nullptr;
    
    hr = converter->Initialize(frame.Get(), GUID_WICPixelFormat32bppPBGRA,
                              WICBitmapDitherTypeNone, nullptr, 0.0f,
                              WICBitmapPaletteTypeCustom);
    if (FAILED(hr)) return nullptr;
    
    // In fallback mode, we don't have m_d2dContext yet, so we can't create Direct2D bitmaps here
    // For now, just return nullptr and handle this in the drawing code
    if (!m_d2dContext) {
        // Store WIC converter for later use in fallback mode
        // For now, return nullptr - we'll handle PNG loading differently in fallback mode
        return nullptr;
    }
    
    // Create Direct2D bitmap from WIC bitmap
    ComPtr<ID2D1Bitmap> bitmap;
    hr = m_d2dContext->CreateBitmapFromWicBitmap(converter.Get(), nullptr, &bitmap);
    if (FAILED(hr)) return nullptr;
    
    return bitmap;
}

void WindowManager::DrawFrame() {
    if (!m_swapChain || !m_d2dContext) return;
    
    HRESULT hr = S_OK;
    
    // Get the back buffer surface
    ComPtr<IDXGISurface> backBuffer;
    hr = m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer));
    if (FAILED(hr)) return;
    
    // Create Direct2D bitmap from the surface
    ComPtr<ID2D1Bitmap1> d2dBackBuffer;
    D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1(
        D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
        D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)
    );
    
    hr = m_d2dContext->CreateBitmapFromDxgiSurface(
        backBuffer.Get(),
        &bitmapProperties,
        &d2dBackBuffer
    );
    if (FAILED(hr)) return;
    
    // Set the back buffer as render target
    m_d2dContext->SetTarget(d2dBackBuffer.Get());
    
    // Begin drawing
    m_d2dContext->BeginDraw();
    
    // Clear with transparent background
    m_d2dContext->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f));
    
    // Choose which image to draw based on neko state (active=neko_up, inactive=neko_down)
    ComPtr<ID2D1Bitmap> currentImage = m_isNekoActive ? m_nekoUpImage : m_nekoDownImage;
    
    if (currentImage) {
        // Draw neko image
        D2D1_RECT_F imageRect = D2D1::RectF(0.0f, 0.0f, 80.0f, 80.0f);
        m_d2dContext->DrawBitmap(currentImage.Get(), imageRect);
    } else {
        // Fallback: draw placeholder content
        ComPtr<ID2D1SolidColorBrush> greenBrush;
        hr = m_d2dContext->CreateSolidColorBrush(
            D2D1::ColorF(D2D1::ColorF::Green, 0.7f), // Semi-transparent green
            &greenBrush
        );
        
        if (SUCCEEDED(hr)) {
            // Draw green rectangle
            D2D1_RECT_F rect = D2D1::RectF(10.0f, 10.0f, 70.0f, 70.0f);
            m_d2dContext->FillRectangle(rect, greenBrush.Get());
            
            // Draw click count indicator
            int currentCount = m_dataManager ? m_dataManager->GetCurrentClickCount() : 0;
            if (currentCount > 0) {
                ComPtr<ID2D1SolidColorBrush> whiteBrush;
                hr = m_d2dContext->CreateSolidColorBrush(
                    D2D1::ColorF(D2D1::ColorF::White),
                    &whiteBrush
                );
                
                if (SUCCEEDED(hr)) {
                    D2D1_RECT_F centerRect = D2D1::RectF(35.0f, 35.0f, 45.0f, 45.0f);
                    m_d2dContext->FillRectangle(centerRect, whiteBrush.Get());
                }
            }
        }
    }
    
    // End drawing
    hr = m_d2dContext->EndDraw();
    
    // Present the frame
    if (SUCCEEDED(hr)) {
        m_swapChain->Present(1, 0); // VSync enabled
        m_dcompDevice->Commit();
    }
}

bool WindowManager::InitializeDirect2DFallback() {
    HRESULT hr = S_OK;
    
    // Initialize COM
    hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) {
        return false;
    }
    
    // Create Direct2D factory
    hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, m_d2dFactory.GetAddressOf());
    if (FAILED(hr)) {
        return false;
    }
    
    // Create WIC factory for PNG loading
    hr = CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER,
                         IID_PPV_ARGS(&m_wicFactory));
    if (FAILED(hr)) {
        return false;
    }
    
    return true;
}

bool WindowManager::DrawDirect2D(HDC hdc) {
    if (!m_d2dFactory || !hdc) return false;
    
    // Create render target for this HDC
    ComPtr<ID2D1DCRenderTarget> dcRenderTarget;
    D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
        D2D1_RENDER_TARGET_TYPE_DEFAULT,
        D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
        96.0f, 96.0f, // Set explicit DPI
        D2D1_RENDER_TARGET_USAGE_NONE,
        D2D1_FEATURE_LEVEL_DEFAULT
    );
    
    HRESULT hr = m_d2dFactory->CreateDCRenderTarget(&props, &dcRenderTarget);
    if (FAILED(hr)) {

        return false;
    }
    
    RECT rc;
    GetClientRect(m_hwnd, &rc);
    hr = dcRenderTarget->BindDC(hdc, &rc);
    if (FAILED(hr)) {

        return false;
    }
    
    // Begin drawing
    dcRenderTarget->BeginDraw();
    
    // Clear with black background (this will be transparent due to color key)
    // Use exact RGB(0,0,0) color for color key transparency
    dcRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 1.0f)); // Black RGB(0,0,0)
    
    // Load PNG images if not already loaded (for fallback mode)
    if (!m_nekoDownImage || !m_nekoUpImage) {
        LoadPNGsForFallback(dcRenderTarget.Get());
    }
    
    // Choose which image to draw based on neko state (active=neko_up, inactive=neko_down)
    ComPtr<ID2D1Bitmap> currentImage = m_isNekoActive ? m_nekoUpImage : m_nekoDownImage;
    

    
    if (currentImage) {
        // Draw neko image without debug background colors
        D2D1_RECT_F imageRect = D2D1::RectF(0.0f, 0.0f, 80.0f, 80.0f);
        dcRenderTarget->DrawBitmap(
            currentImage.Get(), 
            imageRect, 
            1.0f, // opacity
            D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, // Use linear interpolation for better quality
            nullptr // source rect (use entire image)
        );

        
        // Note: Direct2D text rendering is complex, we'll use GDI for text overlay
        // Count display is handled by GDI overlay after Direct2D rendering
    } else {
        // Fallback: draw placeholder content
        ComPtr<ID2D1SolidColorBrush> greenBrush;
        hr = dcRenderTarget->CreateSolidColorBrush(
            D2D1::ColorF(D2D1::ColorF::Green, 1.0f), // Opaque green
            &greenBrush
        );
        
        if (SUCCEEDED(hr)) {
            // Draw green rectangle
            D2D1_RECT_F rect = D2D1::RectF(10.0f, 10.0f, 70.0f, 70.0f);
            dcRenderTarget->FillRectangle(rect, greenBrush.Get());
            
            // Count display will be handled by GDI overlay
        }
    }
    
    // End drawing
    HRESULT endResult = dcRenderTarget->EndDraw();
    if (SUCCEEDED(endResult)) {
        return true;
    } else {
        return false; // Signal failure to trigger GDI fallback
    }
}

void WindowManager::DrawGDIFallback(HDC hdc) {
    
    // Get window size
    RECT clientRect;
    GetClientRect(m_hwnd, &clientRect);
    
    // Fill with black (transparent color)
    HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
    FillRect(hdc, &clientRect, blackBrush);
    DeleteObject(blackBrush);
    
    // Choose which neko image to draw (active=neko_up, inactive=neko_down)
    HBITMAP hNekoImage = m_isNekoActive ? m_hBitmapNekoUp : m_hBitmapNekoDown;
    
    if (hNekoImage) {
        // Draw neko image
        HDC hdcMem = CreateCompatibleDC(hdc);
        HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdcMem, hNekoImage);
        
        // Get bitmap dimensions
        BITMAP bitmap;
        GetObject(hNekoImage, sizeof(BITMAP), &bitmap);
        
        // Use AlphaBlend for proper transparency support
        BLENDFUNCTION blendFunc = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
        AlphaBlend(hdc, 0, 0, 80, 80, hdcMem, 0, 0, bitmap.bmWidth, bitmap.bmHeight, blendFunc);
        
        SelectObject(hdcMem, hOldBitmap);
        DeleteDC(hdcMem);
        

        
        // Draw count display at bottom with 25px margin, white text
        int currentCount = m_dataManager ? m_dataManager->GetCurrentClickCount() : 0;
        if (currentCount > 0) {
            // Create font for count display
            HFONT hFont = CreateFontA(14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, 
                                     DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
                                     CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial");
            HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
            
            // Set text properties - white text with transparent background
            SetBkMode(hdc, TRANSPARENT);
            SetTextColor(hdc, RGB(255, 255, 255)); // White text
            
            // Convert count to string with prefix
            std::string countText = std::to_string(currentCount);
            
            RECT countRect = {0, 80 - 25, 80, 80};
            DrawTextA(hdc, countText.c_str(), -1, &countRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
            
            SelectObject(hdc, hOldFont);
            DeleteObject(hFont);
            

        }
        
    } else {
        // Fallback: only show count when images not loaded
        int currentCount = m_dataManager ? m_dataManager->GetCurrentClickCount() : 0;
        if (currentCount > 0) {
            HFONT hFont = CreateFontA(16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, 
                                     DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
                                     CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial");
            HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
            
            SetBkMode(hdc, TRANSPARENT);
            SetTextColor(hdc, RGB(255, 255, 255)); // White text
            
            std::string countText = "Count: " + std::to_string(currentCount);
            RECT countRect = {0, 80 - 25 - 16, 80, 80 - 25};
            DrawTextA(hdc, countText.c_str(), -1, &countRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
            
            SelectObject(hdc, hOldFont);
            DeleteObject(hFont);
        }

    }
}

void WindowManager::DrawCountOverlay(HDC hdc) {
    // Only draw count display, no background or images
    int currentCount = m_dataManager ? m_dataManager->GetCurrentClickCount() : 0;
    if (currentCount > 0) {
        // Try to create Arial font first, fallback to system default
        HFONT hFont = CreateFontA(14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, 
                                 DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
                                 CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial");
        
        // If Arial failed, try system default fonts
        if (!hFont) {
            hFont = CreateFontA(14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, 
                               DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
                               CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_SWISS, "Tahoma");
        }
        
        // If Tahoma failed, use MS Sans Serif (always available)
        if (!hFont) {
            hFont = CreateFontA(14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, 
                               DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
                               CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_SWISS, "MS Sans Serif");
        }
        
        // Last resort: use system default font
        if (!hFont) {
            hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
        }
        
        HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
        
        // Set text properties - white text with transparent background
        SetBkMode(hdc, TRANSPARENT);
        SetTextColor(hdc, RGB(255, 255, 255)); // White text
        
        // Convert count to string
        std::string countText = std::to_string(currentCount);
        
        // Position at bottom with 25px margin
        RECT countRect = {0, 80 - 25, 80, 80};
        
        // Draw the text
        DrawTextA(hdc, countText.c_str(), -1, &countRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
        
        SelectObject(hdc, hOldFont);
        
        // Only delete font if we created it (not system font)
        if (hFont != GetStockObject(DEFAULT_GUI_FONT)) {
            DeleteObject(hFont);
        }
    }
}

void WindowManager::OnNekoAnimationTimer() {
    // Timer callback - reset neko to inactive state
    if (m_isNekoActive) {
        m_isNekoActive = false;
    if (m_hwnd) {
            // Standard redraw (don't erase background - let Direct2D handle it)
            InvalidateRect(m_hwnd, nullptr, FALSE); // FALSE = don't erase background
            UpdateWindow(m_hwnd);
            // Kill the timer since we're done with it
            KillTimer(m_hwnd, NEKO_TIMER_ID);
        }
    }
}

void WindowManager::HandleTrayIcon(WPARAM wParam, LPARAM lParam) {
    switch (lParam) {
        case WM_RBUTTONUP: {
            // Right-click: show context menu
            POINT pt;
            GetCursorPos(&pt);
            ShowTrayContextMenu(pt.x, pt.y);
            break;
        }
        
        case WM_LBUTTONDBLCLK: {
            // Double-click: toggle window visibility
            if (IsWindowVisible(m_hwnd)) {
                ::ShowWindow(m_hwnd, SW_HIDE);
            } else {
                ::ShowWindow(m_hwnd, SW_SHOW);
                EnsureAlwaysOnTop();
            }
            break;
        }
    }
}

void WindowManager::ShowTrayContextMenu(int x, int y) {
    // Create popup menu
    HMENU hMenu = CreatePopupMenu();
    if (!hMenu) return;
    
    // Add menu items
    AppendMenuA(hMenu, MF_STRING, IDM_RESET_COUNT, "Reset Count");
    AppendMenuA(hMenu, MF_SEPARATOR, 0, nullptr);
    AppendMenuA(hMenu, MF_STRING, IDM_ABOUT, "About");
    AppendMenuA(hMenu, MF_STRING, IDM_EXIT, "Exit");
    
    // Set window to foreground so menu works properly
    SetForegroundWindow(m_hwnd);
    
    // Show menu and get user selection
    int cmd = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_RIGHTBUTTON, x, y, 0, m_hwnd, nullptr);
    
    // Handle menu selection
    switch (cmd) {
        case IDM_RESET_COUNT:
            if (m_resetCallback) {
                m_resetCallback();
            }
            break;
            
        case IDM_ABOUT:
            ShowAboutDialog();
            break;
            
        case IDM_EXIT:
            PostMessage(m_hwnd, WM_DESTROY, 0, 0);
            break;
    }
    
    // Clean up menu
    DestroyMenu(hMenu);
}

void WindowManager::ShowAboutDialog() {
    // Get build timestamp
    char buildDate[64];
    sprintf_s(buildDate, "%s %s", __DATE__, __TIME__);
    
    // Create About dialog message with privacy protection information
    char aboutMessage[1600];
    sprintf_s(aboutMessage, 
        "NekoPa - Keyboard Click Counter V1.1.1\n\n"
        "Build Date: %s\n\n"
        "Author: Luis.Lu\n"
        "License: MIT License\n\n"
        "PRIVACY PROTECTION GUARANTEE:\n"
        "- Only counts keyboard presses, NO key content recorded\n"
        "- All data stored locally in %%APPDATA%%\\NekoPa\\\n"
        "- NO network access, NO data transmission\n"
        "- Source code fully open: gitee.com/LoliClub_Luis/neko-pa\n\n"
        "ANTIVIRUS WARNING EXPLANATION:\n"
        "This program may trigger antivirus warnings because it monitors\n"
        "global keyboard events (for counting only). This is normal behavior\n"
        "for keyboard utilities. Please add to your antivirus whitelist.\n\n"
        "Features:\n"
        "- Cute neko animations with transparent background\n"
        "- System tray integration and always-on-top display\n"
        "- Virtual machine compatibility (VMware, VirtualBox, etc.)\n\n"
        "Technology:\n"
        "- DirectComposition + Direct2D (hardware accelerated)\n"
        "- Intelligent fallback for maximum compatibility\n\n"
        "AI Development: Cursor IDE + Claude-4-Sonnet + Doubao AI\n\n"
        "Copyright (c) 2025 Luis.Lu - All rights reserved.",
        buildDate
    );
    
    // Show About dialog
    MessageBoxA(m_hwnd, aboutMessage, "About NekoPa - Privacy Protected", MB_OK | MB_ICONINFORMATION);
}

void WindowManager::LoadPNGsForFallback(ID2D1DCRenderTarget* renderTarget) {
    if (!renderTarget || !m_wicFactory) return;
    
    // Load neko down image
    m_nekoDownImage = LoadPNGBitmapForFallback(IDR_NEKO_DOWN_PNG, renderTarget);
    
    // Load neko up image
    m_nekoUpImage = LoadPNGBitmapForFallback(IDR_NEKO_UP_PNG, renderTarget);

}

ComPtr<ID2D1Bitmap> WindowManager::LoadPNGBitmapForFallback(int resourceId, ID2D1DCRenderTarget* renderTarget) {
    if (!m_wicFactory || !renderTarget) return nullptr;
    
    // Find and load the resource
    HRSRC hResource = FindResourceA(m_hInstance, MAKEINTRESOURCEA(resourceId), "PNG");
    if (!hResource) {

        return nullptr;
    }
    
    DWORD imageSize = SizeofResource(m_hInstance, hResource);
    if (!imageSize) {

        return nullptr;
    }
    
    HGLOBAL hResourceData = LoadResource(m_hInstance, hResource);
    if (!hResourceData) {

        return nullptr;
    }
    
    void* pImageData = LockResource(hResourceData);
    if (!pImageData) {

        return nullptr;
    }
    
    // Create a stream from the resource data
    ComPtr<IWICStream> stream;
    HRESULT hr = m_wicFactory->CreateStream(&stream);
    if (FAILED(hr)) {

        return nullptr;
    }
    
    hr = stream->InitializeFromMemory(static_cast<BYTE*>(pImageData), imageSize);
    if (FAILED(hr)) {

        return nullptr;
    }
    
    // Create decoder
    ComPtr<IWICBitmapDecoder> decoder;
    hr = m_wicFactory->CreateDecoderFromStream(stream.Get(), nullptr, 
                                              WICDecodeMetadataCacheOnDemand, &decoder);
    if (FAILED(hr)) {

        return nullptr;
    }
    
    // Get the first frame
    ComPtr<IWICBitmapFrameDecode> frame;
    hr = decoder->GetFrame(0, &frame);
    if (FAILED(hr)) {

        return nullptr;
    }
    
    // Create format converter to ensure BGRA format for Direct2D
    ComPtr<IWICFormatConverter> converter;
    hr = m_wicFactory->CreateFormatConverter(&converter);
    if (FAILED(hr)) {

        return nullptr;
    }
    
    hr = converter->Initialize(frame.Get(), GUID_WICPixelFormat32bppPBGRA,
                              WICBitmapDitherTypeNone, nullptr, 0.0f,
                              WICBitmapPaletteTypeCustom);
    if (FAILED(hr)) {

        return nullptr;
    }
    
    // Create D2D bitmap from WIC converter
    ComPtr<ID2D1Bitmap> bitmap;
    hr = renderTarget->CreateBitmapFromWicBitmap(converter.Get(), nullptr, &bitmap);
    if (FAILED(hr)) {

        return nullptr;
    }
    

    return bitmap;
}

void WindowManager::LoadGDIImages() {
    // Load neko images via WIC+GDI
    m_hBitmapNekoDown = LoadPNGResourceAsHBITMAP(IDR_NEKO_DOWN_PNG);
    m_hBitmapNekoUp = LoadPNGResourceAsHBITMAP(IDR_NEKO_UP_PNG);
}

void WindowManager::CleanupGDIImages() {
    if (m_hBitmapNekoDown) {
        DeleteObject(m_hBitmapNekoDown);
        m_hBitmapNekoDown = nullptr;
    }
    
    if (m_hBitmapNekoUp) {
        DeleteObject(m_hBitmapNekoUp);
        m_hBitmapNekoUp = nullptr;
    }
}

HBITMAP WindowManager::LoadPNGResourceAsHBITMAP(int resourceId) {
    if (!m_wicFactory) {
        // Try to create WIC factory on-demand for GDI loading
        HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER,
                                     IID_PPV_ARGS(&m_wicFactory));
        if (FAILED(hr)) {
            return nullptr; // WIC completely unavailable
        }
    }
    
    // Find and load the resource
    HRSRC hResource = FindResourceA(m_hInstance, MAKEINTRESOURCEA(resourceId), "PNG");
    if (!hResource) {

        return nullptr;
    }
    
    DWORD imageSize = SizeofResource(m_hInstance, hResource);
    if (!imageSize) {

        return nullptr;
    }
    
    HGLOBAL hResourceData = LoadResource(m_hInstance, hResource);
    if (!hResourceData) {

        return nullptr;
    }
    
    void* pImageData = LockResource(hResourceData);
    if (!pImageData) {

        return nullptr;
    }
    
    // Create a stream from the resource data
    ComPtr<IWICStream> stream;
    HRESULT hr = m_wicFactory->CreateStream(&stream);
    if (FAILED(hr)) {

        return nullptr;
    }
    
    hr = stream->InitializeFromMemory(static_cast<BYTE*>(pImageData), imageSize);
    if (FAILED(hr)) {

        return nullptr;
    }
    
    // Create decoder
    ComPtr<IWICBitmapDecoder> decoder;
    hr = m_wicFactory->CreateDecoderFromStream(stream.Get(), nullptr, 
                                              WICDecodeMetadataCacheOnDemand, &decoder);
    if (FAILED(hr)) {

        return nullptr;
    }
    
    // Get the first frame
    ComPtr<IWICBitmapFrameDecode> frame;
    hr = decoder->GetFrame(0, &frame);
    if (FAILED(hr)) {

        return nullptr;
    }
    
    // Create format converter to ensure BGRA format
    ComPtr<IWICFormatConverter> converter;
    hr = m_wicFactory->CreateFormatConverter(&converter);
    if (FAILED(hr)) {

        return nullptr;
    }
    
    hr = converter->Initialize(frame.Get(), GUID_WICPixelFormat32bppBGRA,
                              WICBitmapDitherTypeNone, nullptr, 0.0f,
                              WICBitmapPaletteTypeCustom);
    if (FAILED(hr)) {

        return nullptr;
    }
    
    // Get bitmap dimensions
    UINT width, height;
    hr = converter->GetSize(&width, &height);
    if (FAILED(hr)) {
        return nullptr;
    }
    
    // Create DIB section
    BITMAPINFO bmi = {};
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = width;
    bmi.bmiHeader.biHeight = -static_cast<LONG>(height); // Negative for top-down DIB
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 32;
    bmi.bmiHeader.biCompression = BI_RGB;
    
    void* pBits = nullptr;
    HDC hdc = GetDC(nullptr);
    HBITMAP hBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pBits, nullptr, 0);
    ReleaseDC(nullptr, hdc);
    
    if (!hBitmap || !pBits) {
        return nullptr;
    }
    
    // Copy pixel data
    hr = converter->CopyPixels(nullptr, width * 4, width * height * 4, static_cast<BYTE*>(pBits));
    if (FAILED(hr)) {
        DeleteObject(hBitmap);
        return nullptr;
    }
    

    return hBitmap;
}

// Empty implementations for optional methods
HICON WindowManager::CreateIconFromPNGResource(int resourceId) { return nullptr; }
